package com.zhouyong.netty;

import com.zhouyong.message.RpcRequestMessage;
import com.zhouyong.netty.config.ClientChannelInitializer;
import com.zhouyong.netty.handler.CustomizeClientHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.concurrent.DefaultPromise;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.UUID;

/**
 * @author 99Savage
 * @date 2023/3/17 10:39
 */
@Component
@Slf4j
public class NettyClient {
    private static String host;
    private static Integer port;

    @Value("${netty.server.host}")
    public void setHost(String host) {
        NettyClient.host = host;
    }

    @Value("${netty.server.port}")
    public void setPort(Integer port) {
        NettyClient.port = port;
    }

    private static ClientChannelInitializer clientChannelInitializer;

    @Resource
    public void setClientChannelInitializer(ClientChannelInitializer clientChannelInitializer) {
        NettyClient.clientChannelInitializer = clientChannelInitializer;
    }

    private static Channel channel;

    private static final Object LOCK = new Object();

    /**
     * 生成代理对象
     *
     * @param clazz
     * @return
     */
    public static <T> T createProxyInstance(Class<?> clazz) {
        // 获取类加载器
        ClassLoader classLoader = clazz.getClassLoader();
        // 获取接口数组
        Class<?>[] interfaces = new Class[]{clazz};
        try {
            Object o = Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    RpcRequestMessage message = new RpcRequestMessage(
                            UUID.randomUUID().hashCode(),
                            clazz.getName(),
                            method.getName(),
                            method.getReturnType(),
                            method.getParameterTypes(),
                            args
                    );
                    // 发送消息
                    log.info("客户端开始发送消息给服务端===========");
                    getChannel().writeAndFlush(message);

                    // 创建 promise 对象
                    DefaultPromise<Object> promise = new DefaultPromise<>(channel.eventLoop());
                    CustomizeClientHandler.map.put(message.getSequenceId(), promise);

                    // 阻塞客户端写回结果到 promise 中
                    promise.await();
                    if (promise.isSuccess()) {
                        // 调用正常
                        return promise.getNow();
                    } else {
                        // 调用失败
                        throw new RuntimeException(promise.cause());
                    }
                }
            });
            return (T) o;
        } catch (Exception e) {
            log.error("获取代理对象出现异常，cause:【】", e);
        }
        return null;
    }

    public static Channel getChannel() {
        if (channel != null) {
            return channel;
        }
        synchronized (LOCK) {
            if (channel != null) {
                return channel;
            }
            initChannel();
            return channel;
        }
    }

    public static void initChannel() {
        NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap()
                    .group(eventLoopGroup)
                    .channel(NioSocketChannel.class)
                    .handler(clientChannelInitializer);
            // 连接
            channel = bootstrap.connect(host, port).sync().channel();
            channel.closeFuture().addListener(future -> {
                eventLoopGroup.shutdownGracefully();
            });
        } catch (Exception e) {
            log.error("异常: {}", e);
            e.printStackTrace();
        }
    }

}